package com.thx.file;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import com.thx.common.util.ThreadLocalContext;
import com.thx.common.web.WebConstant;


public class StreamUploadServlet extends HttpServlet {
  private static final long serialVersionUID = -8619685235661387895L;
  protected final Logger logger = Logger.getLogger(getClass());

  public void init() throws ServletException {}

  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
      IOException {
    ThreadLocalContext.set(WebConstant.CURRENT_REQUEST, req);
    String type = req.getParameter("type");
    if ("html5".equals(type)) {
      html5Get(req, resp);
    } else if ("breakPoint".equals(type)) {
      breakPointGet(req, resp);
    }
  }

  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
      IOException {
    ThreadLocalContext.set(WebConstant.CURRENT_REQUEST, req);
    String type = req.getParameter("type");
    if ("html5".equals(type)) {
      html5Post(req, resp);
    } else if ("flash".equals(type)) {
      flashPost(req, resp);
    } else {
      breakPointGet(req, resp);
    }
  }

  protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.setContentType("application/json");
    resp.setHeader("Access-Control-Allow-Headers", "Content-Range,Content-Type");
    resp.setHeader("Access-Control-Allow-Origin", Configurations.getCrossOrigins());
    resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
  }

  protected void html5Get(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    doOptions(req, resp);
    String token = req.getParameter("token");
    String modPath = req.getParameter("modPath");
    /*
     * String size = req.getParameter("size"); String fileName = req.getParameter("name");
     */
    PrintWriter writer = resp.getWriter();
    JSONObject json = new JSONObject();
    long start = 0L;
    boolean success = true;
    String message = "";
    try {
      File f = IoUtil.getTokenedFile(token, modPath);
      start = f.length();
      /*
       * if ((token.endsWith("_0")) && ("0".equals(size)) && (0L == start)){//0字节
       * f.renameTo(IoUtil.getFile(fileName)); }
       */
    } catch (FileNotFoundException e) {
      message = "Error: " + e.getMessage();
      success = false;
      logger.info("stream上传组件--找不到文件", e);
    } finally {
      try {
        if (success) {
          json.put("start", start);
        }
        json.put("success", success);
        json.put("message", message);
      } catch (JSONException e) {
        logger.error("stream上传组件--写入json错误", e);
      }
      writer.write(json.toString());
      IoUtil.close(writer);
    }
  }

  protected void html5Post(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    doOptions(req, resp);
    String token = req.getParameter("token");
    String fileName = req.getParameter("name");
    String modPath = req.getParameter("modPath");
    Range range = IoUtil.parseRange(req);
    OutputStream out = null;
    InputStream content = null;
    PrintWriter writer = resp.getWriter();
    JSONObject json = new JSONObject();
    long start = 0L;
    boolean success = true;
    String message = "";
    String fileId = "";
    File f = IoUtil.getTokenedFile(token, modPath);
    try {
      if (f.length() != range.getFrom()) {
        throw new StreamException(StreamException.ERROR_FILE_RANGE_START);
      }
      out = new FileOutputStream(f, true);
      content = req.getInputStream();
      int read = 0;
      byte[] bytes = new byte[10240];
      while ((read = content.read(bytes)) != -1) {
        out.write(bytes, 0, read);
      }
      start = f.length();
    } catch (StreamException e) {
      success = StreamException.ERROR_FILE_RANGE_START == e.getCode();
      message = "Code: " + e.getCode();
      logger.info("stream上传组件--文件的开始位置不对", e);
    } catch (FileNotFoundException e) {
      message = "Code: " + StreamException.ERROR_FILE_NOT_EXIST;
      success = false;
      logger.info("stream上传组件--找不到文件", e);
    } catch (IOException e) {
      message = "IO Error: " + e.getMessage();
      success = false;
      logger.error("stream上传组件--IO错误", e);
    } finally {
      IoUtil.close(out);
      IoUtil.close(content);
      if (range.getSize() == start) {// 已经上传的文件的开始端与文件大小相等时，证明已经上传完成
        File dst = IoUtil.getFile(fileName, modPath);
        dst.delete();
        f.renameTo(dst);
        com.thx.file.model.File dataFile = IoUtil.saveFile(dst, fileName);
        if (dataFile != null) {
          fileId = dataFile.getFileId();
        }
      }
      try {
        if (success) {
          json.put("fileId", fileId);
          json.put("start", start);
        }
        json.put("success", success);
        json.put("message", message);
      } catch (JSONException e) {
      }
      writer.write(json.toString());
      IoUtil.close(writer);
    }
  }

  protected void breakPointGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    String name = req.getParameter("name");
    String size = req.getParameter("size");
    String isLogin = req.getParameter("isLogin");
    String modPath = req.getParameter("modPath");
    String token = TokenUtil.generateToken(name, size, req, isLogin, modPath);
    PrintWriter writer = resp.getWriter();
    JSONObject json = new JSONObject();
    try {
      json.put("token", token);
      json.put("success", true);
      json.put("message", "");
    } catch (JSONException e) {
      logger.error("stream上传组件--写json数据出错", e);
    }
    writer.write(json.toString());
  }

  protected void flashPost(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    super.doOptions(req, resp);
    req.setCharacterEncoding("utf8");
    PrintWriter writer = resp.getWriter();
    com.thx.file.model.File dataFile;
    String fileId = "";
    boolean isMultipart = ServletFileUpload.isMultipartContent(req);
    if (!isMultipart) {
      logger.error("stream上传组件--form表单不支持上传");
      return;
    }
    JSONObject json = new JSONObject();
    long start = 0L;
    boolean success = true;
    String message = "";

    ServletFileUpload upload = new ServletFileUpload();
    InputStream in = null;
    String token = null;
    String modPath = null;
    try {
      FileItemIterator iter = upload.getItemIterator(req);
      while (iter.hasNext()) {
        FileItemStream item = iter.next();
        String name = item.getFieldName();
        in = item.openStream();
        if (item.isFormField()) {
          String value = Streams.asString(in);
          if ("token".equals(name)) {
            token = value;
          }
          if ("modPath".equals(name)) {
            modPath = value;
          }
        } else {
          String fileName = item.getName();
          dataFile = IoUtil.streaming(in, token, fileName, modPath);
          if (dataFile != null) {
            start = dataFile.getFileBytes();
            fileId = dataFile.getFileId();
          }
        }
      }
    } catch (FileUploadException e) {
      success = false;
      message = "Error: " + e.getLocalizedMessage();
      logger.error("stream上传组件--上传文件出错", e);
    } finally {
      try {
        if (success) {
          json.put("fileId", fileId);
          json.put("start", start);
        }
        json.put("success", success);
        json.put("message", message);

      } catch (JSONException e) {
        logger.info("stream上传组件--写入json数据出错");
      }
      writer.write(json.toString());
      IoUtil.close(in);
      IoUtil.close(writer);
    }
  }

  public void destroy() {
    super.destroy();
  }

  protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
      IOException {
    super.doHead(req, resp);
  }
}
